/** 
 * CharsetSelectorComboBox.java
 * @author Lex
 * 2007-9-8 ����08:01:52
 */
package org.lex.swing.widgets;

import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeListener;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Locale;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.SwingUtilities;

public class CharsetSelectorComboBox extends JComboBox {
	public static final String SELECTED_CHARSET_CHANGED_PROPERTY = "selectedCharsetChangedProperty";
	private static final Charset[] DEFAULT_CHARSETS;
	static {
		String[] names = { "US-ASCII", "ISO-8859-1", "UTF-8", "UTF-16" };
		ArrayList<Charset> list = new ArrayList<Charset>(names.length + 1);
		list.add(Charset.defaultCharset());
		for (String name : names) {
			try {
				Charset c = Charset.forName(name);
				if (null != c)
					list.add(c);
			} catch (Exception e) {
			}
		}
		DEFAULT_CHARSETS = list.toArray(new Charset[list.size()]);
	}
	// ====================================================
	// XXX do we need to sort the model by display names?
	private DefaultComboBoxModel model = null;

	public CharsetSelectorComboBox() {
		this(DEFAULT_CHARSETS);
	}

	public CharsetSelectorComboBox(Charset[] data) {
		super();
		this.setEditable(false);
		if (null == data)
			data = DEFAULT_CHARSETS;
		model = new DefaultComboBoxModel();
		model.addElement(new MoreEntry());
		for (int i = 0; i < data.length; i++) {
			Charset cs = data[i];
			if (null != cs)
				this.addLast(cs);
		}
		this.setModel(model);
		this.setSelectedIndex(0);
		this.addActionListener(new ActionHandler());
	}

	public void addCharsetChangeListener(PropertyChangeListener l) {
		super.addPropertyChangeListener(SELECTED_CHARSET_CHANGED_PROPERTY, l);
	}

	public void removeCharsetChangeListener(PropertyChangeListener l) {
		super.removePropertyChangeListener(SELECTED_CHARSET_CHANGED_PROPERTY, l);
	}

	public void addFirst(Charset charset) {
		this.insertCharset(charset, 0);
	}

	public void addLast(Charset charset) {
		int index = model.getSize() - 1;
		if (index < 0)
			index = 0;
		this.insertCharset(charset, index);
	}

	public void insertCharset(Charset charset, int index) {
		model.insertElementAt(new CharsetEntry(charset), index);
	}

	public Charset getSelectedCharset() {
		Object o = this.getSelectedItem();
		return ((CharsetEntry) o).charset;
	}

	/**
	 * set the selected charset.<br>
	 * this will add the <code>charset</code> into combobox if it not exists in
	 * the combobox.
	 * 
	 * @param charset
	 */
	public void setSelectedCharset(Charset charset) {
		int index = this.getCharsetIndex(charset);
		if (-1 == index) {
			// not found, we will insert it
			this.addFirst(charset);
			index = 0;
		}
		this.setSelectedIndex(index);
	}

	public int getCharsetIndex(Charset charset) {
		if (null == charset)
			return -1;
		int size = this.model.getSize();
		for (int i = 0; i < size; i++) {
			CharsetEntry entry = (CharsetEntry) model.getElementAt(i);
			if (charset.equals(entry.charset))
				return i;
		}
		return -1;
	}

	public boolean contains(Charset charset) {
		return -1 != this.getCharsetIndex(charset);
	}

	protected void fireCharsetChanged() {
		Charset cs = this.getSelectedCharset();
		super.firePropertyChange(SELECTED_CHARSET_CHANGED_PROPERTY, null, cs);
	}

	class ActionHandler implements ActionListener {
		private int lastSelectedIndex = 0;

		public void actionPerformed(ActionEvent e) {
			int index = getSelectedIndex();
			if (-1 == index)
				return;
			// selected a normal item
			if (index != model.getSize() - 1) {
				this.lastSelectedIndex = index;
				fireCharsetChanged();
				return;
			}
			// selected the "More..." item
			setPopupVisible(false);
			Charset[] cs = JCharsetChooser.showMultipleSelection(root());
			if (0 == cs.length) {
				// canceld the select more charsets action
				setSelectedIndex(lastSelectedIndex);
				return;
			}
			// add selected charsets to end of model, and selected the first one
			for (int i = 0; i < cs.length; i++)
				if (false == contains(cs[i]))
					addLast(cs[i]);
			setSelectedCharset(cs[0]);
			fireCharsetChanged();
		}

		private Window root() {
			return SwingUtilities.getWindowAncestor(CharsetSelectorComboBox.this);
		}
	}
}

class CharsetEntry {
	Charset charset = null;
	String displayName = "";

	CharsetEntry(Charset charset) {
		this.charset = charset;
		this.displayName = charset.displayName(Locale.getDefault());
	}

	public String toString() {
		return displayName;
	}
}

class MoreEntry extends CharsetEntry {
	MoreEntry() {
		super(Charset.defaultCharset());
	}

	public String toString() {
		return "More...";
	}
}
